#include "matrix.h"
#include "vector.h"

#include <cstdlib>
#include <iostream>

namespace t2c2 {

Matrix::Matrix(const unsigned int& _row,const unsigned int& _column, const unsigned int& _type) : row(_row), column(_column) {
	data = new double*[row];

	for(unsigned int i = 0 ; i < row ; i++)
		data[i] = new double[column];
	
	for(unsigned int i = 0; i < row; i++)
	  for(unsigned int j = 0; j < column; j++)
	    data[i][j] = 0;
	switch(_type)	{
	  case NORMAL_MATRIX:
	    break;
	  case IDENTITY_MATRIX:
	    if(row != column)
	      exit(1);
	    for(unsigned int i = 0;i<row;i++)
	      data[i][i] = 1;
	    break;
	}
}

Matrix::Matrix(const Matrix& _m) {
	row = _m.row;
	column = _m.column;
	
	data = new double*[row];

	for(unsigned int i = 0; i < row; i++)
		data[i] = new double[column];
	
	for(unsigned int i = 0; i < row; i++)
	  for(unsigned int j = 0; j < column; j++)
	    data[i][j] = _m[i][j];
}

Matrix::~Matrix(void) {
	for(unsigned int i = 0; i < row; i++)
		delete[] data[i];
	delete[] data;
}

unsigned int Matrix::get_row(void) const {
	return row;
}

unsigned int Matrix::get_column(void) const {
	return column;
}

void Matrix::print(void) const {
	for(unsigned int i=0 ; i<row ; i++) {
		for(unsigned int j=0 ; j<column ; j++)
			std::cout << data[i][j] << "\t";
		std::cout << std::endl;
	}
}

double* Matrix::operator[](unsigned int i) const {
	return data[i];
}

Matrix Matrix::operator-(void) const {
	Matrix result(row,column);
	
	for(unsigned int i=0; i<row; i++)
		for(unsigned int j=0; j<column; j++)
			result[i][j] = - data[i][j];
	
	return result;
}

Matrix Matrix::transpose(void) const {
  Matrix result(column, row);
  
  for(unsigned int i = 0; i < row; i++)
    for(unsigned int j = 0; j < column; j++) 
      result[j][i] = (*this)[i][j];
  return result;
}

Vector Matrix::to_vector(unsigned int _column) const {
  Vector result(row);
  
  for(unsigned int i = 0; i < row; i++) {
    result[i] = data[i][_column];
  }
  
  return result;
}

Matrix& Matrix::operator= (const Matrix& _m) {
	for(unsigned int i = 0; i < row; i++)
		delete[] data[i];
	delete[] data;
	
	row = _m.get_row();
	column = _m.get_column();
	
	data = new double*[row];
	
	for(unsigned int i = 0; i < row; i++)
		data[i] = new double[column];
	
	for(unsigned int i = 0; i < row; i++)
		for(unsigned int j = 0; j < column; j++)
			data[i][j] = _m[i][j];

	return (*this);
}


Matrix operator+(const Matrix& _matrix1,const Matrix& _matrix2) {
	if( _matrix1.get_row() == _matrix2.get_row()  && _matrix1.get_column() == _matrix2.get_column()) {
		unsigned int row = _matrix1.get_row();
		unsigned int column = _matrix1.get_column();
		Matrix result(row,column);
		
		for(unsigned int i=0; i<row; i++)
			for(unsigned int j=0; j<column; j++)
				result[i][j] = _matrix1[i][j] + _matrix2[i][j];
		
		return result;
	}
	else {
		exit(1);
	}
}

Matrix operator-(const Matrix & _matrix1,const Matrix & _matrix2) {
	if( _matrix1.get_row() == _matrix2.get_row()  && _matrix1.get_column() == _matrix2.get_column()) {
		unsigned int row = _matrix1.get_row();
		unsigned int column = _matrix1.get_column();
		Matrix result(row,column);
		
		for(unsigned int i=0; i<row; i++)
			for(unsigned int j=0; j<column; j++)
				result[i][j] = _matrix1[i][j] - _matrix2[i][j];
		
		return result;
	}
	else {
		exit(1);
	}
}


Matrix operator*(const Matrix & _matrix1,const Matrix & _matrix2) {
	if(_matrix1.get_column() != _matrix2.get_row()) {
		exit(1);
	}
	unsigned int row = _matrix1.get_row();
	unsigned int column = _matrix2.get_column();
	Matrix result(row,column);
	
	for(unsigned int i = 0; i < row; i++) {
		for(unsigned int j = 0; j < column; j++) {
			result[i][j] = 0;
			for(unsigned int k = 0; k < _matrix1.get_column(); k++)
				result[i][j] = result[i][j] + _matrix1[i][k] * _matrix2[k][j];
		}
	}
	return result;
}

Matrix operator*(const Matrix& _matrix,double _value) {
	Matrix result = _matrix;
	for(unsigned int i =0; i < result.get_row(); i++) {
		for(unsigned int j = 0; j < result.get_column(); j++) {
			result[i][j] = _matrix[i][j]*_value;
		}
	}
	return result;
}

}
